#include <vector>
#include <cstdint>
#include <string.h>
#include <fstream>

static const uint32_t _magic_numbers[] = 
{
        0x67452301,
        0xefcdab89,
        0x98badcfe,
        0x10325476,
};

uint32_t to_little_endian(uint32_t v)
{
    uint32_t le;
    auto p = reinterpret_cast<uint8_t*>(&le);
    p[0] = v & 0xff;
    p[1] = v >> 8 & 0xff;
    p[2] = v >> 16 & 0xff;
    p[3] = v >> 24;
    return le;
}

// 使用时最好只传入最后一块的数据和总的长度
//
// 填充到 64 的倍数，length 为原始数据字节数
static void padBlock(uint8_t block[], uint32_t &n, uint64_t length)
{
    // （以位为单位）
    length <<= 3;

    // 计算需要填充的字节数
    uint32_t  paddingLength = 64 - (n+8)%64;

    // 添加 0x80 字节
    block[n] = 0x80;

    // 添加 0x00 字节
    memset(block+n+1, 0x00, paddingLength-1);
    n += paddingLength; //填充后的blcok长度

    // 添加原始数据长度（64 位，小端序）
    auto p = reinterpret_cast<uint32_t*>(block+n);
    p[0] = to_little_endian(length & 0xffffffff);
    p[1] = to_little_endian(length >> 32);
    n += 8;
}

static inline uint32_t rotateLeft(uint32_t x, uint32_t n)
{
    return (x << n) | (x >> (32-n));        
}

static void blockGeneric(std::vector<uint32_t> &state, uint8_t block[])
{
    uint32_t x[16];
    for(int i = 0; i < 64; i+=4) 
    {
        x[i >> 2] = block[i] | (uint32_t)block[i+1] << 8 | (uint32_t)block[i+2] << 16 | (uint32_t)block[i+3] << 24;
    }

    uint32_t a = state[0], b = state[1], c = state[2], d = state[3];

    // round 1
    a = (~b&d | c&b) + x[0] + 0xD76AA478 + a;
    a = rotateLeft(a, 7) + b;
    d = (~a&c | b&a) + x[1] + 0xE8C7B756 + d;
    d = rotateLeft(d, 12) + a;
    c = (~d&b | d&a) + x[2] + 0x242070DB + c;
    c = rotateLeft(c, 17) + d;
    b = (~c&a | d&c) + x[3] + 0xC1BDCEEE + b;
    b = rotateLeft(b, 22) + c;
    a = (~b&d | c&b) + x[4] + 0xF57C0FAF + a;
    a = rotateLeft(a, 7) + b;
    d = (~a&c | b&a) + x[5] + 0x4787C62A + d;
    d = rotateLeft(d, 12) + a;
    c = (~d&b | d&a) + x[6] + 0xA8304613 + c;
    c = rotateLeft(c, 17) + d;
    b = (~c&a | d&c) + x[7] + 0xFD469501 + b;
    b = rotateLeft(b, 22) + c;
    a = (~b&d | c&b) + x[8] + 0x698098D8 + a;
    a = rotateLeft(a, 7) + b;
    d = (~a&c | b&a) + x[9] + 0x8B44F7AF + d;
    d = rotateLeft(d, 12) + a;
    c = (~d&b | d&a) + x[10] + 0xFFFF5BB1 + c;
    c = rotateLeft(c, 17) + d;
    b = (~c&a | d&c) + x[11] + 0x895CD7BE + b;
    b = rotateLeft(b, 22) + c;
    a = (~b&d | c&b) + x[12] + 0x6B901122 + a;
    a = rotateLeft(a, 7) + b;
    d = (~a&c | b&a) + x[13] + 0xFD987193 + d;
    d = rotateLeft(d, 12) + a;
    c = (~d&b | d&a) + x[14] + 0xA679438E + c;
    c = rotateLeft(c, 17) + d;
    b = (~c&a | d&c) + x[15] + 0x49B40821 + b;
    b = rotateLeft(b, 22) + c;

    // round 2
    a = (~d&c | d&b) + x[1] + 0xF61E2562 + a;
    a = rotateLeft(a, 5) + b;
    d = (~c&b | c&a) + x[6] + 0xC040B340 + d;
    d = rotateLeft(d, 9) + a;
    c = (~b&a | d&b) + x[11] + 0x265E5A51 + c;
    c = rotateLeft(c, 14) + d;
    b = (~a&d | c&a) + x[0] + 0xE9B6C7AA + b;
    b = rotateLeft(b, 20) + c;
    a = (~d&c | d&b) + x[5] + 0xD62F105D + a;
    a = rotateLeft(a, 5) + b;
    d = (~c&b | c&a) + x[10] + 0x02441453 + d;
    d = rotateLeft(d, 9) + a;
    c = (~b&a | d&b) + x[15] + 0xD8A1E681 + c;
    c = rotateLeft(c, 14) + d;
    b = (~a&d | c&a) + x[4] + 0xE7D3FBC8 + b;
    b = rotateLeft(b, 20) + c;
    a = (~d&c | d&b) + x[9] + 0x21E1CDE6 + a;
    a = rotateLeft(a, 5) + b;
    d = (~c&b | c&a) + x[14] + 0xC33707D6 + d;
    d = rotateLeft(d, 9) + a;
    c = (~b&a | d&b) + x[3] + 0xF4D50D87 + c;
    c = rotateLeft(c, 14) + d;
    b = (~a&d | c&a) + x[8] + 0x455A14ED + b;
    b = rotateLeft(b, 20) + c;
    a = (~d&c | d&b) + x[13] + 0xA9E3E905 + a;
    a = rotateLeft(a, 5) + b;
    d = (~c&b | c&a) + x[2] + 0xFCEFA3F8 + d;
    d = rotateLeft(d, 9) + a;
    c = (~b&a | d&b) + x[7] + 0x676F02D9 + c;
    c = rotateLeft(c, 14) + d;
    b = (~a&d | c&a) + x[12] + 0x8D2A4C8A + b;
    b = rotateLeft(b, 20) + c;

    // round 3
    a = (d ^ c ^ b) + x[5] + 0xFFFA3942 + a;
    a = rotateLeft(a, 4) + b;
    d = (c ^ b ^ a) + x[8] + 0x8771F681 + d;
    d = rotateLeft(d, 11) + a;
    c = (d ^ b ^ a) + x[11] + 0x6D9D6122 + c;
    c = rotateLeft(c, 16) + d;
    b = (d ^ c ^ a) + x[14] + 0xFDE5380C + b;
    uint32_t magic = rotateLeft(b, 23) + c;
    a = a + 0xA4BEEA44 + (d ^ c ^ magic) + x[1];
    b = rotateLeft(a, 4) + magic;
    d = (c ^ magic ^ b) + x[4] + 0x4BDECFA9 + d;
    d = rotateLeft(d, 11) + b;
    c = (d ^ magic ^ b) + x[7] + 0xF6BB4B60 + c;
    c = rotateLeft(c, 16) + d;
    a = magic + 0xBEBFBC70 + (d ^ c ^ b) + x[10];
    a = rotateLeft(a, 23) + c;
    b = (d ^ c ^ a) + x[13] + 0x289B7EC6 + b;
    b = rotateLeft(b, 4) + a;
    d = (c ^ a ^ b) + x[0] + 0xEAA127FA + d;
    d = rotateLeft(d, 11) + b;
    c = (d ^ a ^ b) + x[3] + 0xD4EF3085 + c;
    c = rotateLeft(c, 16) + d;
    a = a + 0x04881D05 + (d ^ c ^ b) + x[6];
    a = rotateLeft(a, 23) + c;
    b = (d ^ c ^ a) + x[9] + 0xD9D4D039 + b;
    b = rotateLeft(b, 4) + a;
    d = (c ^ a ^ b) + x[12] + 0xE6DB99E5 + d;
    d = rotateLeft(d, 11) + b;
    c = (d ^ a ^ b) + x[15] + 0x1FA27CF8 + c;
    c = rotateLeft(c, 16) + d;
    a = (d ^ c ^ b) + x[2] + 0xC4AC5665 + a;
    a = rotateLeft(a, 23) + c;

    // round 4
    b = ((~d | a) ^ c) + x[0] + 0xF4292244 + b;
    b = rotateLeft(b, 6) + a;
    d = ((~c | b) ^ a) + x[7] + 0x432AFF97 + d;
    d = rotateLeft(d, 10) + b;
    c = ((~a | d) ^ b) + x[14] + 0xAB9423A7 + c;
    c = rotateLeft(c, 15) + d;
    a = ((~b | c) ^ d) + x[5] + 0xFC93A039 + a;
    a = rotateLeft(a, 21) + c;
    b = ((~d | a) ^ c) + x[12] + 0x655B59C3 + b;
    b = rotateLeft(b, 6) + a;
    d = ((~c | b) ^ a) + x[3] + 0x8F0CCC92 + d;
    d = rotateLeft(d, 10) + b;
    c = ((~a | d) ^ b) + x[10] + 0xFFEFF47D + c;
    c = rotateLeft(c, 15) + d;
    a = ((~b | c) ^ d) + x[1] + 0x85845DD1 + a;
    a = rotateLeft(a, 21) + c;
    b = ((~d | a) ^ c) + x[8] + 0x6FA87E4F + b;
    b = rotateLeft(b, 6) + a;
    d = ((~c | b) ^ a) + x[15] + 0xFE2CE6E0 + d;
    d = rotateLeft(d, 10) + b;
    c = ((~a | d) ^ b) + x[6] + 0xA3014314 + c;
    c = rotateLeft(c, 15) + d;
    a = ((~b | c) ^ d) + x[13] + 0x4E0811A1 + a;
    a = rotateLeft(a, 21) + c;
    b = ((~d | a) ^ c) + x[4] + 0xF7537E82 + b;
    b = rotateLeft(b, 6) + a;
    d = ((~c | b) ^ a) + x[11] + 0xBD3AF235 + d;
    d = rotateLeft(d, 10) + b;
    c = ((~a | d) ^ b) + x[2] + 0x2AD7D2BB + c;
    c = rotateLeft(c, 15) + d;
    a = ((~b | c) ^ d) + x[9] + 0xEB86D391 + a;
    a = rotateLeft(a, 21) + c;

    state[0] += b;
    state[1] += a;
    state[2] += c;
    state[3] += d;
}

// 计算校验和
std::vector<uint32_t> CheckSumStream(std::ifstream& file)
{
    std::vector<uint32_t> state(std::begin(_magic_numbers), std::end(_magic_numbers));
    // 实现校验和计算逻辑
    uint8_t block[128];
    uint32_t n = 0;
    uint64_t len = 0;
    while(!file.eof())
    {
        file.read((char *)block, 64);
        n = file.gcount();
        len += n;
        if(n != 64)
        {
            break;
        }
        else
        {
            blockGeneric(state, block);
            n = 0;
        }
    }
    padBlock(block, n, len);
    for(int i=0; i<n; i+=64)
    {
        blockGeneric(state, block+i);
    }
    return state;
}


std::vector<uint32_t> CheckSum(uint8_t block[], uint32_t len)
{
    std::vector<uint32_t> state(std::begin(_magic_numbers), std::end(_magic_numbers));

    // 先计算前面部分
    uint32_t n = 0;
    for(; n+64 <= len; n+=64)
    {
        blockGeneric(state, block+n);
    }

    uint8_t padded[128];
    memcpy(padded, block+n, len-n);
    n = len - n ;
    padBlock(padded, n, len);
    for(int i=0; i<n; i+=64)
    {
        blockGeneric(state, block+i);
    }
    return state;
}
